package pl.edu.uj.tcs.blef.server;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map.Entry;

import pl.edu.uj.tcs.blef.protocol.DealEndRequestFactory;
import pl.edu.uj.tcs.blef.protocol.InvalidParametersErrorFactory;
import pl.edu.uj.tcs.blef.protocol.NewHandRequestFactory;
import pl.edu.uj.tcs.blef.protocol.NotYourTurnErrorFactory;
import pl.edu.uj.tcs.blef.protocol.NullRequestHandler;
import pl.edu.uj.tcs.blef.protocol.Request;
import pl.edu.uj.tcs.blef.protocol.StatusRequestFactory;
import pl.edu.uj.tcs.blef.protocol.TableInfoRequestFactory;
import pl.edu.uj.tcs.blef.protocol.UnknownRequestErrorFactory;
import pl.edu.uj.tcs.blef.utils.Bet;
import pl.edu.uj.tcs.blef.utils.BetPool;
import pl.edu.uj.tcs.blef.utils.Card;
import pl.edu.uj.tcs.blef.utils.Deck;
import pl.edu.uj.tcs.blef.utils.Hand;

public final class Game {
	private Table table;
	private Deck deck;
	private HashMap<Client, Hand> hands;
	private Client clientTurn;
	private BetPool betPool;
	
	public Game(Table table){
		System.out.println("New game...");
		this.table = table;
		this.deck = new Deck();
		this.hands = new HashMap<Client, Hand>();
		this.betPool = new BetPool();
		
		System.out.println("Adding players...");
		for(Client c: this.table.getAwaitingPlayers()){
			this.table.getPlayers().add(c);
		}
		
		System.out.println("Determine begining player...");
		this.table.getAwaitingPlayers().clear();
		this.clientTurn = this.table.getLastTurnPlayer();
		if(this.clientTurn == null) this.clientTurn = this.table.getOperator();
		
		int index = 0;
		for(int i=0; i<this.table.getPlayers().size(); ++i){
			if(this.table.getPlayers().get(i).equals(clientTurn)){
				index = i;
				break;
			}
		}
		
		index = (index+1)%this.table.getPlayers().size();
		this.table.setLastTurnPlayer(this.table.getPlayers().get(index));
		
		System.out.println("Deal cards...");
		for(Client c: table.getPlayers()){
			Hand hand = new Hand(c.getInitialHandSize());
			hand.drawCards(deck);
			hands.put(c, hand);
			try {
				Object[] param = new Object[2];
				param[0] = hand.getHandSize();
				param[1] = hand.getCards();
					
				Request r = new NewHandRequestFactory().build(param, new NullRequestHandler());
				r.send(new PrintWriter(c.getSocket().getOutputStream()));
			} catch(Exception e){ System.out.println(e.getMessage());}
		}
		
		for(Client c: table.getLostPlayers()){
			Hand hand = new Hand(1);
			hand.drawCards(deck);
			hands.put(c, hand);
			try {
				Object[] param = new Object[2];
				param[0] = hand.getHandSize();
				param[1] = hand.getCards();
					
				Request r = new NewHandRequestFactory().build(param, new NullRequestHandler());
				r.send(new PrintWriter(c.getSocket().getOutputStream()));
			} catch(Exception e){ System.out.println(e.getMessage());}
		}
		
		for(Client c: this.table.getLostPlayers()){
			this.table.getPlayers().add(c);
		}
		this.table.getLostPlayers().clear();
		
		try {
			String[] param = new String[1]; 
			param[0] = "Your turn";
				
			Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
			r.send(new PrintWriter(this.clientTurn.getSocket().getOutputStream()));
		} catch(Exception e){}
		
		
	}
	
	public HashMap<Client, Hand> getHands(){
		return this.hands;
	}
	
	public void setNewDeal(){
		System.out.println("New deal...");
		this.deck = new Deck();
		this.betPool = new BetPool();
		
		for(Client c: this.table.getAwaitingPlayers()){
			this.table.getPlayers().add(c);
			this.hands.put(c, new Hand(c.getInitialHandSize()));
		}
		
		System.out.println("Determine begining player...");
		this.table.getAwaitingPlayers().clear();
		this.clientTurn = this.table.getLastTurnPlayer();
		if(this.clientTurn == null || table.getLostPlayers().contains(this.clientTurn)) this.clientTurn = this.table.getOperator();
		
		int index = 0;
		for(int i=0; i<this.table.getPlayers().size(); ++i){
			if(this.table.getPlayers().get(i).equals(clientTurn)){
				index = i;
				break;
			}
		}
		
		index = (index+1)%this.table.getPlayers().size();
		this.table.setLastTurnPlayer(this.table.getPlayers().get(index));
		
		for(Entry<Client, Hand> c: hands.entrySet()){
			c.getValue().setCards(new LinkedList<Card>());
			c.getValue().drawCards(deck);
			
			try {
				Object[] param = new Object[2];
				param[0] = c.getValue().getHandSize();
				param[1] = c.getValue().getCards();
					
				Request r = new NewHandRequestFactory().build(param, new NullRequestHandler());
				r.send(new PrintWriter(c.getKey().getSocket().getOutputStream()));
			} catch(Exception e){ System.out.println(e.getMessage());}
		}
		
		try {
			String[] param = new String[1]; 
			param[0] = "Your turn";
				
			Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
			r.send(new PrintWriter(this.clientTurn.getSocket().getOutputStream()));
		} catch(Exception e){}
	}
	
	public BetPool getBetPool(){
		return this.betPool;
	}
	
	public boolean newBet(Client client, Bet bet){
		if(!this.clientTurn.equals(client)){
			try {
				Request r = new NotYourTurnErrorFactory().build(null, new NullRequestHandler());
				r.send(new PrintWriter(client.getSocket().getOutputStream()));
			} catch (Exception e){}
			
			return false;
			
		} else {
			if(betPool.newBet(client, bet)){
				for(Client c: this.hands.keySet()){
					try {
						String[] param = new String[1]; param[0] = client.getNick() + " bets " + bet.getCode();
						Request r = new TableInfoRequestFactory().build(param, new NullRequestHandler());
						r.send(new PrintWriter(c.getSocket().getOutputStream()));
					} catch (Exception e){}
				}
				
				int index = 0;
				for(int i=0; i<this.table.getPlayers().size(); ++i){
					if(this.table.getPlayers().get(i).equals(clientTurn)){
						index = i;
						break;
					}
				}
				
				index = (index+1)%this.table.getPlayers().size();
				this.clientTurn = this.table.getPlayers().get(index);
				
				try {
					String[] param = new String[1]; 
					param[0] = "Your turn";
						
					Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
					r.send(new PrintWriter(this.clientTurn.getSocket().getOutputStream()));
				} catch(Exception e){}
				
				return true;
				
			} else {
				try {
					Request r = new InvalidParametersErrorFactory().build(null, new NullRequestHandler());
					r.send(new PrintWriter(client.getSocket().getOutputStream()));
				} catch (Exception e){}
				
				return false;
			}
		}
	}
	
	public boolean checkForLosers(){
		if(hands.size() <= 4){
			Iterator<Entry<Client, Hand>> it = hands.entrySet().iterator();
			while(it.hasNext()){
				Entry<Client, Hand> e = it.next();
				if(e.getValue().getHandSize() >= 6){
					try {
						String[] param = new String[1]; param[0] = "Game lost";
						Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
						r.send(new PrintWriter(e.getKey().getSocket().getOutputStream()));
					} catch (Exception ex){}
					
					table.getLostPlayers().add(e.getKey());
					table.getPlayers().remove(e.getKey());
					it.remove();
				}
			}
		} else {
			Iterator<Entry<Client, Hand>> it = hands.entrySet().iterator();
			int cardSum = 0; int maxSize = 0;
			
			while(it.hasNext()){
				Entry<Client, Hand> e = it.next();
				if(e.getValue().getHandSize() > maxSize) maxSize = e.getValue().getHandSize();
				cardSum += e.getValue().getHandSize();
			}
			
			if(cardSum >= 24){
				it = hands.entrySet().iterator();
				while(it.hasNext()){
					Entry<Client, Hand> e = it.next();
					if(e.getValue().getHandSize() == maxSize){
						try {
							String[] param = new String[1]; param[0] = "Game lost";
							Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
							r.send(new PrintWriter(e.getKey().getSocket().getOutputStream()));
						} catch (Exception ex){}
						
						table.getLostPlayers().add(e.getKey());
						table.getPlayers().remove(e.getKey());
						it.remove();
					}
				}
			}
		}
		
		return false;
	}
	
	public boolean check(Client client){
		if(!this.clientTurn.equals(client)){
			try {
				Request r = new NotYourTurnErrorFactory().build(null, new NullRequestHandler());
				r.send(new PrintWriter(client.getSocket().getOutputStream()));
			} catch (Exception e){}
			
			return false;
			
		} else {
			if(betPool.size() == 0){
				try {
					Request r = new UnknownRequestErrorFactory().build(null, new NullRequestHandler());
					r.send(new PrintWriter(client.getSocket().getOutputStream()));
				} catch (Exception e){}
				
				return false;
			} else {
				LinkedList<Card> cards = new LinkedList<Card>();
				for(Entry<Client, Hand> hand : hands.entrySet()){
					for(Card c: hand.getValue().getCards()){
						cards.add(c);
					}
				}
				
				if(betPool.check(cards)){
					StringBuilder sb = new StringBuilder();
					sb.append("PLAYERS: " + hands.size());
					sb.append('\n');
					
					for(Entry<Client, Hand> e : hands.entrySet()){
						sb.append(e.getKey().getNick() + '\n');
						sb.append(e.getValue().toString() + '\n');
						
						if(e.getKey().equals(client)){
							try {
								String[] param = new String[1];	param[0] = "You have lost";
								Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
								r.send(new PrintWriter(client.getSocket().getOutputStream()));
							} catch (Exception ex){}
							
							e.getValue().incrementHandSize();
							
						} else {
							try {
								String[] param = new String[1];	param[0] = "You have won";
								Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
								r.send(new PrintWriter(betPool.getLastPlayer().getSocket().getOutputStream()));
							} catch (Exception ex){}
						}
					}
					
					
					for(Client c: table.getPlayers()){
						try {
							String[] param = new String[1];	param[0] = sb.toString().trim();
							Request r = new DealEndRequestFactory().build(param, new NullRequestHandler());
							r.send(new PrintWriter(c.getSocket().getOutputStream()));
						} catch (Exception ex){}
					}
					
					for(Client c: table.getLostPlayers()){
						try {
							String[] param = new String[1];	param[0] = sb.toString().trim();
							Request r = new DealEndRequestFactory().build(param, new NullRequestHandler());
							r.send(new PrintWriter(c.getSocket().getOutputStream()));
						} catch (Exception ex){}
					}
					
					checkForLosers();
					return false;
					
				} else {
					StringBuilder sb = new StringBuilder();
					sb.append("PLAYERS: " + hands.size());
					sb.append('\n');
					
					for(Entry<Client, Hand> e : hands.entrySet()){
						sb.append(e.getKey().getNick() + '\n');
						sb.append(e.getValue().toString() + '\n');
						
						if(e.getKey().equals(client)){
							try {
								String[] param = new String[1];	param[0] = "You have won";
								Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
								r.send(new PrintWriter(client.getSocket().getOutputStream()));
							} catch (Exception ex){}
							
						} else {
							try {
								String[] param = new String[1];	param[0] = "You have lost";
								Request r = new StatusRequestFactory().build(param, new NullRequestHandler());
								r.send(new PrintWriter(betPool.getLastPlayer().getSocket().getOutputStream()));
							} catch (Exception ex){}
							
							e.getValue().incrementHandSize();
						}
					}
					
					
					for(Client c: table.getPlayers()){
						try {
							String[] param = new String[1];	param[0] = sb.toString().trim();
							Request r = new DealEndRequestFactory().build(param, new NullRequestHandler());
							r.send(new PrintWriter(c.getSocket().getOutputStream()));
						} catch (Exception ex){}
					}
					
					for(Client c: table.getLostPlayers()){
						try {
							String[] param = new String[1];	param[0] = sb.toString().trim();
							Request r = new DealEndRequestFactory().build(param, new NullRequestHandler());
							r.send(new PrintWriter(c.getSocket().getOutputStream()));
						} catch (Exception ex){}
					}
					
					checkForLosers();
					return true;
				}
			}
		}
	}
}
